#!/usr/bin/env python3

import socket
import urllib.parse
import ssl

# see https://github.com/python/cpython/blob/master/Lib/urllib/parse.py
urllib.parse.uses_relative.append("gemini")
urllib.parse.uses_netloc.append("gemini")

#!/usr/bin/python3
import cgi, os, json, socket, ssl, threading, time
from urllib.parse import urlparse, parse_qs, urlencode, uses_relative, uses_netloc

uses_relative.append("gemini")
uses_netloc.append("gemini")


class StartComparison(str):
    def __eq__(self, lhs):
        return lhs.startswith(self)


class ResponseCodes:
    # use like: response.statusCode==ResponseCodes.GENERIC_SUCCESS
    GENERIC_INPUT = StartComparison("1")
    GENERIC_SUCCESS = StartComparison("2")
    GENERIC_REDIRECT = StartComparison("3")
    GENERIC_TEMPFAIL = StartComparison("4")
    GENERIC_PERMFAIL = StartComparison("5")
    GENERIC_CERTFAIL = StartComparison("6")

    # the rest of these are just normal strings
    INPUT_NEEDED = "10"
    INPUT_NEEDED_SENSITIVE = "11"

    SUCCESS = "20"

    REDIRECT_TEMP = "30"
    REDIRECT_PERM = "31"

    TEMPFAIL_GENERIC = "40"
    TEMPFAIL_SERVER_UNAVAILABLE = "41"
    TEMPFAIL_CGI_ERROR = "42"
    TEMPFAIL_PROXY_ERROR = "43"
    TEMPFAIL_SLOW_DOWN = "44"

    PERMFAIL_GENERIC = "50"
    PERMFAIL_NOT_FOUND = "51"
    PERMFAIL_GONE = "52"
    PERMFAIL_PROXY_REFUSED = "53"
    PERMFAIL_BAD_REQUEST = "59"

    CERTFAIL_NEED_CERT = "60"
    CERTFAIL_BAD_CERT = "61"
    CERTFAIL_INVALID_CERT = "62"

    @classmethod
    def code_to_name(cls,code):
        codes = {v: k for k, v in cls.__dict__.items() if type(v)==str and v.isdigit()}
        try:
            return codes[code]
        except KeyError:
            pass
        try:
            return codes[str(code)]
        except KeyError as e:
            raise RuntimeError(f"Invalid response code {e}!") from None

# If we want to keep an SSL context for some time, we need to instantiate this...

# Methods:
# - request(url, optionally a client cert, optionally server cert)
#   - returns a response object: { status, meta, mimetype, body (byte array), servercert }
#
# - response(socket, status, meta, optionally body (byte array) )
#   - returns success or failure?
#
# - genCert(...)
#   - returns a cert (string?)
class ResponseObject():
    def __init__(self, serverCert, statusCode, metaField, mimeType, messageBody = ""):
        self.serverCert = serverCert
        self.statusCode = statusCode
        self.metaField = metaField
        self.mimeType = mimeType
        self.messageBody = messageBody

def request(url, clientCert = "", clientKey = "", serverCert = ""):
    parsed = urllib.parse.urlparse(url)

    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.minimum_version = ssl.TLSVersion.TLSv1_2

    if clientCert and clientKey:
        context.load_cert_chain(clientCert, clientKey)

    if serverCert:
        context.load_verify_locations(serverCert)
    else:
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE

    sock = socket.create_connection((parsed.hostname, parsed.port or 1965))
    ssock = context.wrap_socket(sock, server_hostname=parsed.hostname)
    ssock.sendall((url+"\r\n").encode("UTF-8"))

    return ssock.makefile(mode = "rb")

